package org.budo.ehcache.replicator;

import java.util.Properties;

import org.budo.support.spring.context.aware.BudoApplicationContextAware;
import org.springframework.cache.interceptor.CacheEvictOperation;

import lombok.extern.slf4j.Slf4j;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import net.sf.ehcache.event.CacheEventListener;
import net.sf.ehcache.event.CacheEventListenerFactory;

/**
 * 产生利用Redis时间通知来同步分布式缓存的监听器
 * 
 * @author lmw
 */
@Slf4j
public class EhcacheReplicatorFactory extends CacheEventListenerFactory implements CacheEventListener {
    private static final String NOTIFY_ELEMENT_REMOVED = "notifyElementRemoved";

    private static final String NOTIFY_REMOVE_ALL = "notifyRemoveAll";

    private static int _log_count = 0;

    /**
     * 控制日志量
     */
    private static void _log(String message) {
        if (_log_count < 100 //
                || (_log_count < 1000 && _log_count % 10 == 0) //
                || (_log_count < 10000 && _log_count % 100 == 0))
            log.error(message + ", _log_count=" + _log_count);
    }

    private EhcacheReplicator getEhcacheReplicator() {
        return BudoApplicationContextAware.getBeanCached_2(EhcacheReplicator.class);
    }

    /**
     * CacheEventListener return this
     */
    public CacheEventListener createCacheEventListener(Properties properties) {
        return this;
    }

    /**
     * Spring的clear操作走的是EhCache的removeAll方法
     * 
     * @see org.springframework.cache.ehcache.EhCacheCache#evict(Object)
     * @see org.springframework.cache.interceptor.CacheAspectSupport#performCacheEvict(CacheOperationContext,CacheEvictOperation,Object)
     */
    public void notifyRemoveAll(Ehcache cache) {
        EhcacheReplicator replicator = this.getEhcacheReplicator();
        if (null == replicator) {
            _log("#45 notifyRemoveAll error, EhcacheRedisReplicator.getInstance() is null, cache=" + cache);
            return;
        }

        ReplicatorMessage message = ReplicatorMessage.newMessage(cache, null, NOTIFY_REMOVE_ALL);
        replicator.publishReplicatorMessage(message);
    }

    /**
     * 单个缓存过期事件
     */
    public void notifyElementRemoved(Ehcache cache, Element element) throws CacheException {
        EhcacheReplicator replicator = this.getEhcacheReplicator();
        if (null == replicator) {
            _log("#65 notifyElementRemoved error, EhcacheRedisReplicator.getInstance() is null, cache=" + cache + ", element=" + element);
            return;
        }

        ReplicatorMessage message = ReplicatorMessage.newMessage(cache, element, NOTIFY_ELEMENT_REMOVED);
        replicator.publishReplicatorMessage(message);
    }

    /**
     * 收到消息后的统一处理
     */
    public static void onReplicatorMessage(ReplicatorMessage message, EhcacheReplicator ehcacheReplicator) {
        CacheManager cacheManager = BudoApplicationContextAware.getBeanCached(CacheManager.class);

        String cacheName = message.getCacheName();
        Cache cache = cacheManager.getCache(cacheName);
        if (null == cache) {
            log.error("#46 onRedisMessage, cache[" + cacheName + "] not found, message=" + message);
            return;
        }

        if (NOTIFY_ELEMENT_REMOVED.equals(message.getEvent())) {
            cache.remove(message.getElementKey()); // 会进入 notifyElementRemoved 事件，由 publishRedisMessage 方法作筛选判断，不再次广播
        } else if (NOTIFY_REMOVE_ALL.equals(message.getEvent())) {
            cache.removeAll(); // 会进入 notifyRemoveAll 事件，由 publishRedisMessage 方法作筛选判断，不再次广播
        } else {
            log.error("#55 onRedisMessage, event else, message=" + message + ", ehcacheReplicator=" + ehcacheReplicator);
        }
    }

    /**
     * 无处理
     */
    public void notifyElementUpdated(Ehcache cache, Element element) throws CacheException {
    }

    /**
     * 无处理
     */
    public void notifyElementPut(Ehcache cache, Element element) throws CacheException {
    }

    /**
     * 过期应该各管各的，不同步
     */
    public void notifyElementExpired(Ehcache cache, Element element) {
    }

    /**
     * Spring的evict操作走的是EhCache的remove方法
     * 
     * @see org.springframework.cache.ehcache.EhCacheCache#evict(Object)
     */
    public void notifyElementEvicted(Ehcache cache, Element element) {
    }

    /**
     * 接口方法空实现
     */
    public void dispose() {
    }

    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}